package com.opengl.camera;

import java.nio.ByteBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.opengl.Matrix;
import android.opengl.GLES20;
import android.util.Log;

/*
 * This class handles all the top level GLRenderer calls.
 * This is where the GL stuff gets initialized such
 * as the viewport and clear colors.  Also the actual calls
 * to draw our objects each frame are done here.
 */
public class CameraRenderer extends GLRenderer {
	// objects to render
	// private Triangle m_triangle;
	// private Square m_square;
	private Context m_context;
	private SquareCameraTextured m_cameraFrame;

	// model view projection matrices
	private float[] m_mvpMatrix = new float[16];
	private float[] m_projMatrix = new float[16];
	private float[] m_vMatrix = new float[16];
	private float m_screenRatio;

	// misc texture vars and flags
	static boolean m_firstFrame = true;
	private int[] m_textureIds;
	private final int m_numTextures = 1; // always at least 1 for the camera texture + whatever else

	public CameraRenderer(Context context) {
		m_context = context;
	}

	@Override
	public void onDrawFrame(GL10 unused) {
		GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
		
		renderCameraPreview();
	}

	private void renderCameraPreview() {
		// Draw camera preview
		//Matrix.setIdentityM(m_projMatrix, 0);
		Matrix.orthoM(m_projMatrix, 0, -0.5f, 0.5f, -0.5f, 0.5f, -1.0f, 1.0f);
		Matrix.setIdentityM(m_vMatrix, 0);
		Matrix.multiplyMM(m_mvpMatrix, 0, m_projMatrix, 0, m_vMatrix, 0);
		
		//final float ccdWidthMM = CameraParams.sensorWidthMM;
		//float localScaleMatrix[] = { ccdWidthMM, ccdWidthMM, ccdWidthMM};
		float localScaleMatrix[] = { 1.0f, 1.0f, 1.0f };
		m_cameraFrame.draw(m_mvpMatrix, 0, localScaleMatrix);

	}
	
	@Override
	public void onSurfaceCreated(GL10 unused, EGLConfig config) {
		GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		m_firstFrame = true;
		initTextures();
		// m_triangle = new Triangle();
		// m_square = new Square();
		m_cameraFrame = new SquareCameraTextured(m_textureIds[0], 0);
	}

	@Override
	public void onSurfaceChanged(GL10 unused, int width, int height) {
		// Adjust the viewport based on geometry changes,
		// such as screen rotation
		GLES20.glViewport(0, 0, width, height);
		GLES20.glEnable(GLES20.GL_DEPTH_TEST);
		GLES20.glDepthFunc(GLES20.GL_LEQUAL);
		GLES20.glFrontFace(GLES20.GL_CCW);
		// Enable back face culling
		GLES20.glCullFace(GLES20.GL_BACK);
		GLES20.glEnable(GLES20.GL_CULL_FACE);
		

		m_firstFrame = true;
		initTextures();

		m_screenRatio = (float) width / (float) height;

		// this projection matrix is applied to object coordinates
		// in the onDrawFrame() method
		//Matrix.perspectiveM(m_projMatrix, 0, CameraParams.cameraParams.getHorizontalViewAngle(), m_screenRatio, 0.0001f, 10.0f);
		//Matrix.orthoM(m_projMatrix, 0, -0.5f, 0.5f, -0.5f, 0.5f, -1.0f, 1.0f);
	}

	public static int loadShader(int type, String shaderCode) {

		// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
		// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
		int shader = GLES20.glCreateShader(type);

		// add the source code to the shader and compile it
		GLES20.glShaderSource(shader, shaderCode);
		GLES20.glCompileShader(shader);

		return shader;
	}

	/**
	 * Utility method for debugging OpenGL calls. Provide the name of the call
	 * just after making it:
	 * 
	 * <pre>
	 * mColorHandle = GLES20.glGetUniformLocation(mProgram, &quot;vColor&quot;);
	 * MyGLRenderer.checkGlError(&quot;glGetUniformLocation&quot;);
	 * </pre>
	 * 
	 * If the operation is not successful, the check throws an error.
	 * 
	 * @param glOperation
	 *            - Name of the OpenGL call to check.
	 */
	public static void checkGlError(String glOperation) {
		int error;
		while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
			Log.e("CameraRenderer", glOperation + ": glError " + error);
			throw new RuntimeException(glOperation + ": glError " + error);
		}
	}

	public void initTextures() {
		if (m_textureIds == null)
		{
			m_textureIds = new int[m_numTextures];

			// generate texture handle IDs
			GLES20.glGenTextures(m_numTextures, m_textureIds, 0);
		}
		else
		{
			GLES20.glDeleteTextures(m_numTextures, m_textureIds, 0);
		}

		int overlayId = m_textureIds[1];
		GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, overlayId);
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
	}
	
	/**
	 * Generates a texture from the byte array sent from the CameraGLSurfaceView
	 * 
	 * @param glCameraFrame
	 * @param rows
	 * @param cols
	 */
	public void updateTexture(byte[] glCameraFrame) {
		// if we never generated our textures create a new texture handle
		if (m_firstFrame)
		{
			m_firstFrame = false;

			// set the camera texture
			int camTexId = m_textureIds[0];
			int texSize = CameraParams.textureSize;
			GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, camTexId);
			GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
			GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
			GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, texSize, texSize, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, ByteBuffer.wrap(glCameraFrame));
			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);			
		} 
		else {
			// update any textures that need updating
			// specifically the camera texture update
			int texSize = CameraParams.textureSize;
			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_textureIds[0]);
			GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, texSize, texSize, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, ByteBuffer.wrap(glCameraFrame));

			// update other textures - probably not needed for now
			// GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, otherTexId);
			// GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
			// otherTexSz.x, otherTexSz.y, 0, GLES20.GL_LUMINANCE,
			// GLES20.GL_UNSIGNED_BYTE, ByteBuffer.wrap(glCameraFrame));
		}
	}
}